package org.NooLab.structures;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

import org.NooLab.utilities.datatypes.ValuePairs ;

import org.NooLab.utilities.files.PathFinder;
import org.NooLab.utilities.files.DFutils;

import org.NooLab.utilities.logging.LogControl;
import org.NooLab.utilities.logging.PrintLog;
import org.NooLab.utilities.strings.StringsUtil;

import org.apache.commons.collections.BidiMap;
import org.apache.commons.collections.bidimap.DualHashBidiMap;





@SuppressWarnings("rawtypes")
public abstract class ArgumentsCliReaderAbstract<T> implements ArgumentsPublicIntf{

	
	public final String _APP_COMPLETION_EXIT    = InstanceProcessControlIntf._APP_COMPLETION_EXIT ;
	public final String _APP_COMPLETION_SERVICE = InstanceProcessControlIntf._APP_COMPLETION_SERVICE ;
	
	public final String  _APP_CONTEXT_ITEXX = InstanceProcessControlIntf._APP_CONTEXT_ITEXX ;
	public final String  _APP_CONTEXT_ALONE = InstanceProcessControlIntf._APP_CONTEXT_ALONE ;

	
	
	ArgumentsPublicIntf parent ;
	
	Map<String,T> argMap = new TreeMap<String, T>();
	
	public String configFile  = "astor-config.xml";
	public String homeDir     = "" ;
	public String projectName = "itx" ;
	public String flavor      = "" ; 
	public String jarDir      = "";
	public String contextMode = "";
	public String appsecret   = "" ; 
	public int    waitFor     = -1 ;
	public String processName ="" ;
	public int    logLevel = 2;
	
	public String reserved    = "" ;
	public ValuePairs fieldBag = new ValuePairs ();
	
	public int inPort=-1,outPort=-1;

	public int[] ports = new int[]{0,0};
	public int[] servicePorts = new int[]{0,0};
	public int[] tikaPorts = new int[]{0,0}; 
	
	public int startSomClients=0; // e.g. in case of Astor L1-Som
	
	public ArrayList<String> topics = new ArrayList<String>(); 
	protected ArrayList<String> remainingTopics = new ArrayList<String>();
	
	BidiMap rawStringMap = new DualHashBidiMap();
	
	private Object argumentsObj;
	private boolean startApplet = false;
	
	protected boolean appletRequested=false;
	protected boolean defaultsRequested=false;

	protected int resumeMode;
	protected int nodeCount;
	protected String onCompletion ;

	protected String sourceFileDedicated;
	protected String sourceFilesByFolder;
	protected String sourceFileByListingFile;

	
	protected DFutils fileutil = new DFutils();
	protected StringsUtil strgutil = new StringsUtil();

	
	
	// ========================================================================	
	public ArgumentsCliReaderAbstract(){
		
	}
	// ========================================================================

	protected void setParent( ArgumentsPublicIntf parent ) {
		this.parent = parent;
	}
	
	protected Object getReference(){
		return argumentsObj;
	}
	protected void setReference(Object argobj){
		argumentsObj = argobj;
	}
	
	protected abstract String[] getDescription() ;
	
	public void showArguments( String[] description){
		System.err.println("You have to use start parameters: \n"); PrintLog.Delay(50) ;
		System.out.println(description[0]);
		System.out.println(description[1]);
		System.out.println(description[2]);
	}
	

	@Override
	public String showActualArguments(int printimmediately) {
		String topic,value,params="  ";
		Object obj;
		ArrayList<String> vtopics = new ArrayList<String> ();
		
		// Map<String,T> argMap
		Iterator it = argMap.entrySet().iterator();
		while (it.hasNext()) {
			Map.Entry pairs = (Map.Entry) it.next();
			
			topic = (String) pairs.getKey();
			
					obj = pairs.getValue();
					
			value = strgutil.getStringFromT(obj, "") ;
			
			if (vtopics.indexOf(topic.toLowerCase())<0){
				params = params + "-"+topic+": "+value+" \n  ";
			}
			
			vtopics.add(topic.toLowerCase());
			
			// it.remove(); // avoids a ConcurrentModificationException
		}

		if (printimmediately>0){
			System.out.println(params) ;
		}
		return params;
	}
	
	 
	
	public String[] loadArgumentsFromFile( String argStr) throws IOException{
		String[] cliArguments = new String[0];
		
		String filename,path ;
		
		filename = "NooLabTikaServer.clia" ;
		
		PathFinder pf = new PathFinder();
		
		path = pf.getAppBinPath(this.getClass(), true);
		 	   if (path.contains("java/workspace")){
		 		   path = "D:/data/iTexx/bin" ;
		 	   }
		 	   if(path.contains(".jar")){ 
		 		   path = fileutil.getparentdir(path);
		 	   }
		System.out.println( "path : "+path) ;
		
		filename = fileutil.createpath( path, filename);
		
		if (fileutil.fileexists(filename)){
			String str ;
			str = fileutil.readFile2String(filename) ;
			str = strgutil.replaceAll(str, "\n", " ");
			
			cliArguments = str.split(" -");
			
			for (int i=0;i<cliArguments.length;i++){
				cliArguments[i] = cliArguments[i].trim();
				if ((cliArguments[i].length()>0) && (cliArguments[i].trim().startsWith("-")==false)){
					cliArguments[i] = "-"+cliArguments[i] ;
				}
			}
		}
		
		return cliArguments ;
	}
	
	
	@Override
	public Map<String,T> getArgumentsMap(){
		
		return argMap;
	}
	

	@Override
	public Object getMappedTopic(String[] topics) {
		
		 
		Object obj = null;
		
		for (int i=0;i<topics.length;i++){
			obj = getMappedTopic( topics[i]) ;
			if (obj!=null){
				break;
			}
		}
		
		return obj;
	}

	
	@Override
	public Object getMappedTopic(String topic) {


		boolean hb1,hb2=false;
		Object obj = null;
		
		try{
		

			hb1 = argMap.containsKey(topic);
			if (hb1==false){
				hb2 = argMap.containsKey(topic.toLowerCase());
			}
			if (hb1){
				obj = argMap.get(topic);
			}
			if (hb2){
				obj = argMap.get(topic.toLowerCase());
			}

		}catch(Exception e){
			e.printStackTrace();
			String estr="";
			obj = estr; 
		}
		
		return obj ;
	}


	@Override
	public boolean contains(String topic) {
	
		boolean result=false;

		result = argMap.containsKey(topic);
		
		return result;
	}

	protected void defineArguments(String[] argumentTopics) {
		// 
		
		topics = new ArrayList<String>(Arrays.asList(argumentTopics)) ;
		
		remainingTopics = new ArrayList<String>() ;
		for (int i=0;i<topics.size();i++){
			remainingTopics.add( topics.get(i).toLowerCase()) ;	
		}
	}

	
	@SuppressWarnings({ "unchecked"  })
	protected ArgumentsPublicIntf prepareArguments( String homedir, String[] _args) throws Exception {


		String[] _arg ;
		String sep, avStr, str ;
		
		int k ;
		homeDir = homedir;
		
		
		
		
		
		
		
		ArrayList<String> args = new ArrayList<String>( Arrays.asList(_args));


		
		
		k=0;
		while (k<args.size()){
			
			str = args.get(k).trim() ;
			if (str.startsWith("-")==false){
				if (k>0){
					if (args.get(k-1).endsWith(":")==false){
						sep=":";
					}else{
						sep=" ";
					}
					str = args.get(k-1) + sep + args.get(k);
					args.set(k-1,str) ;
					args.remove(k);
					k--;
				}
			}
			k++;
		}


		for (String argument : args){
			argument = argument.trim();
			
			// _arg = strgutil.splitQ( argument, ":" ) ;
			_arg = argument.split(":",2);
			
			if ((_arg[0].length()>0) && (_arg[0].startsWith("-")) ){
				str = _arg[0].trim() ;
				str = str.substring(1,str.length()) ;
				
				avStr = "";
				if ((_arg.length>1) && (_arg[1].length()>0)){
					avStr = _arg[1].trim() ;
				}
				avStr = strgutil.trimm(avStr, "\"");
				rawStringMap.put(str.toLowerCase(),avStr);
				
				
				if (avStr.startsWith("\"")){
					avStr = avStr.substring(1,str.length()) ;
				}
				if (avStr.endsWith("\"")){
					avStr = avStr.substring(0,str.length()-1) ;
				}

				if (handlingTreatment( str, "waitfor") ){
					int argval = 0;
					// -waitfor: adkhf-23487asdf-3487sdfr-324
					if (avStr.length()==0){
						argval = 0;
					}else{
						if (strgutil.isNumericX(avStr)){
							argval = strgutil.getNumFromStr(avStr, 0) ;
						}
					}
					waitFor = argval;
					argMap.put( "waitfor", (T)(Integer)waitFor) ;
					
				}
				
				if (handlingTreatment( str, "appsecret") ){
					
					// -waitfor: adkhf-23487asdf-3487sdfr-324
					appsecret = avStr;
					argMap.put( "appsecret", (T)(String)appsecret) ;
					
				}
				
				if (handlingTreatment( str, "name") ){
					// -name:astor
					processName = avStr;
					argMap.put( "name", (T)(String)processName) ;
				}
				
				
				if (handlingTreatment(str, "homedir") ){
					
					if (args.size()>2){
						
						for (int n=2;n<_arg.length;n++){
							if (_arg[n].length()>0){
								avStr= avStr + _arg[n] ;
								if (k<args.size()-1){
									avStr=avStr+":" ;			
								}
							}
						}
					}
					if (DFutils.folderExists(avStr)){
						homeDir = avStr ; homedir = avStr ;
						argMap.put( "homeDir", (T)homeDir) ;
						argMap.put( "homedir", (T)homeDir) ;
						
					}
				}

				
				if ((handlingTreatment(str, "size")) || (handlingTreatment(str, "nodecount") )){
					 
					nodeCount = strgutil.getNumFromStr(avStr, -1);
					argMap.put( "nodeCount", (T)(Integer)nodeCount) ;
					argMap.put( "nodecount", (T)(Integer)nodeCount) ;
				}

				
				
				if (handlingTreatment(str, "resume") ){
					 
					resumeMode = strgutil.getNumFromStr(avStr, 0);
					argMap.put( "resume", (T)(Integer)resumeMode) ;
				}
				
				if (handlingTreatment(str, "sourcemode") ){
					if (avStr.toLowerCase().startsWith("catalog")){
						
					}else{
					//  -sourcemode: catalog|[ file|folder,"[fpath]" ]|[list,"filename"]
						int d = _getInfoAboutExplicitSource(avStr);
						/*
						 *  d : 1 = file,  field = sourceFileDedicated ; 
						 *  	2 = folder,field = sourceFilesByFolder; 
						 *  	3 = list,  field = sourceFileByListingFile; 
						 */
						argMap.put( "sourcemode", (T)homeDir) ;
					}
					
				}
				
				// onCompletion:exit
				if (handlingTreatment(str, "oncompletion") ){
					int oncVal=1;
					if (strgutil.isNumericX(avStr) ){
						oncVal = strgutil.getNumFromStr(avStr, 1);
					}
					if ((avStr.toLowerCase().contentEquals("exit")) ||
						(oncVal<=0)){
						onCompletion = parent.get_APP_COMPLETION_EXIT();	
					}else{
						onCompletion = parent.get_APP_COMPLETION_SERVICE();
					}
					argMap.put( "oncompletion", (T)onCompletion) ;
				} 
				
				if (handlingTreatment(str, "context") ){
					
					if (avStr.toLowerCase().contains("itexx")){
						contextMode = parent.get_APP_CONTEXT_ITEXX() ;// = "itexx"; 
					}
					if (avStr.toLowerCase().contains("alone")){
						contextMode = parent.get_APP_CONTEXT_ALONE();
					}
					argMap.put( "context", (T)contextMode) ;
				}

				int vi;
				if (handlingTreatment(str, "loglevel") ){  // default = 2
					
					vi = LogControl.Level ; 
					if ((avStr.length()>0) &&(strgutil.isNumericX(avStr))){
						vi = Integer.parseInt(avStr) ; 
					}else{
						vi=2;
					}
					
					if ((vi>=0) && (vi<=5)){
						LogControl.Level = vi;
						LogControl.setLevel(vi);
						logLevel=vi;
					}
					argMap.put( "loglevel", (T)(Integer)logLevel) ;
				}
				
				if (handlingTreatment(str, "comm")){
					// -comm: in-7209,out-7210	
					 
					ports = _acquirePorts( "comm", avStr);
					outPort = ports[1] ;
					inPort = ports[0];  
					
				}
				if (handlingTreatment(str, "serviceports")){
					servicePorts = _acquirePorts( "serviceports", avStr);
					 
				}
				
				if (handlingTreatment(str, "notify")){
					throw(new Exception("'notify' is a discarded argument"));
				}
				
				
				if ( (handlingTreatment(str, "cfg")) ){

					String cfgFile  = avStr; 
					if (DFutils.fileExists( cfgFile )==false){
						str = DFutils.createPath( homeDir, projectName+"/config/" ) ;
						cfgFile = DFutils.createPath( str , cfgFile) ;
						if (DFutils.fileExists( cfgFile ) ){
							configFile = cfgFile;
						}
					}else{
						configFile = cfgFile;
					}
					argMap.put( "cfg", (T)configFile) ;
				}
				

				if (handlingTreatment(str, "applet")){
					appletRequested  =true ;
					argMap.put( "applet", (T)(Boolean)appletRequested) ;
				}
				
				if (handlingTreatment(str, "default")){
					defaultsRequested =true ;
					argMap.put( "default", (T)(Boolean)defaultsRequested) ;
				}
				
			} // _arg defined ?
			
		}// ->
		
		str = "";
		
		return this;
	} 
	
	
	protected int[] _acquirePorts(String portname, String avStr) {
		 
		int[] ports = new int[2] ;
		avStr = avStr.replace(",:", ",");
		String[] portStr = avStr.split(",");
		
		
		for (int i=0;i<2;i++){
			String pstr = portStr[i].toLowerCase();
			if (pstr.indexOf("in")>=0){
				pstr = pstr.replace("in", ""); pstr = strgutil.trimm(pstr, ":") ;
				pstr = pstr.replace("-", "");
				if (strgutil.isNumericX(pstr)){
					ports[0] = strgutil.getNumFromStr(pstr, 7000);
					
				}
			}
			if (pstr.indexOf("out")>=0){
				pstr = pstr.replace("out", ""); pstr = strgutil.trimm(pstr, ":") ;
				pstr = pstr.replace("-", "");
				if (strgutil.isNumericX(pstr)){
					ports[1] = strgutil.getNumFromStr(pstr, 7000);
					
				}
			}
		}
		argMap.put( portname, (T)ports) ;
		
		return ports;
	}
	
	
	protected boolean handlingTreatment(String inStr, String[] markers) {
		boolean hb=false;
		
		
		for (int i=0;i<markers.length;i++){
			hb = handlingTreatment( inStr, markers[i]) ;
			if (hb){
				break;
			}
		}// i->
			
			
		return hb;
	}
	
	protected boolean handlingTreatment(String inStr, String marker) {
		int p;
		boolean hb;
		
		
		hb = inStr.toLowerCase().contentEquals(marker);
		
		if (hb){
			p = remainingTopics.indexOf( marker );
			if (p>=0){
				remainingTopics.remove(p) ;
			}

		}
		return hb;
	}
	
	
	@SuppressWarnings("unchecked")
	protected boolean handlingTreatment( String[] markers) {
		
		String avStr, str ;
		 
		boolean hb=false;
		 
		try{
			

			avStr = getArgumentValueStr( markers );
		 
			
			for (int i=0;i<markers.length;i++){
				
				if (rawStringMap.containsKey(markers[i])){
					
					avStr = (String) rawStringMap.get(markers[i]) ;
					if (avStr.length()>0){
						str = (String) rawStringMap.getKey(avStr) ;
						hb = handlingTreatment( str, markers[i]);	
					}// sth found
					
					if (hb){
						  
						argMap.put( markers[i], (T) avStr );
						
						int p = remainingTopics.indexOf( markers[i] );
						if (p<0){
							p = remainingTopics.indexOf( markers[i].toLowerCase() );
						}
						if (p>=0){
							remainingTopics.remove(p) ;
						}
						break;
					}	
				} // marker in raw map ?
				
			}
	
		}catch(Exception e){
			String estr = "Arguments::handlingTreatment(): " ;
			PrintLog.Print(1, estr) ;
			e.printStackTrace();
		}
		
		return hb;
	}

	
	
	 
	abstract protected ArgumentsPublicIntf parse(String[] _args) ;
		
	
	
	protected String getArgumentValueStr(String[] inStrings) {

		String str, avStr="";
		
		for (int i=0;i<inStrings.length;i++){
			str = inStrings[i] ;
			if (rawStringMap.containsKey(str)){
				avStr = (String) rawStringMap.get(str);
				break;
			}
		}
		
		return avStr;
	}

	
	private int _getInfoAboutExplicitSource(String sourceArg) {
		
		String[] avs;
		String str="";
		int p, resultD=-7;
		
		p= sourceArg.indexOf("=");
		if (p==0){
			sourceArg = "file="+sourceArg ;
		}
		//int q = sourceArg.indexOf("\"");
		
		avs = sourceArg.split("=");
		str = avs[1].trim();
		str = strgutil.replaceAll(str, "\"", "").trim();
		
		if (avs[0].toLowerCase().startsWith("file")){
			// -sourcemode: file,"[filepath]"
			resultD=-1;
			if (DFutils.fileExists(str)){
				sourceFileDedicated = str;
				resultD=1;
			}
			 
		}  
		if (avs[0].toLowerCase().startsWith("folder")){
			// -sourcemode: file,"[folder]"
			resultD=-2;
			if ((DFutils.folderExists(str)==false) && (DFutils.fileExists(str))){
				str = DFutils.getParentDir(str) ;
			}
			if (DFutils.folderExists(str)){
				sourceFilesByFolder = str ;
				resultD=2;
			}
		}
		if (avs[0].toLowerCase().startsWith("list")){
			// -sourcemode: file,"[folder]"
			resultD=-3;
			if (DFutils.fileExists(str)){
				sourceFileByListingFile = str ;
				resultD=3;
			}
		}
		
		
		return resultD ;
	}


	@Override
	public String get_APP_COMPLETION_EXIT() {
		return _APP_COMPLETION_EXIT;
	}

	@Override
	public String get_APP_COMPLETION_SERVICE() {
		return _APP_COMPLETION_SERVICE;
	}

	@Override
	public String get_APP_CONTEXT_ITEXX() {
		return _APP_CONTEXT_ITEXX;
	}

	@Override
	public String get_APP_CONTEXT_ALONE() {
		return _APP_CONTEXT_ALONE;
	}

	

	@Override
	public int count() {
		
		int size=-1;
		
		if (argMap!=null){
			size = argMap.size();
		}
				
		return size;
	}



	@Override
	public String getProjectName() {
		 
		return projectName;
	}
	@Override
	public String getConfigFile() {
		//  
		return configFile;
	}
	@Override
	public void setProjectName(String prjname) {
		projectName = prjname ;
	}
	@Override
	public String getHomeDir() {
		
		return homeDir;
	}
	@Override
	public void setConfigFile(String cfgfname) {
		configFile = cfgfname;
	}
	@Override
	public String getContextMode() {
		
		return contextMode;
	}
	@Override
	public boolean startApplet() {
		
		return startApplet;
	}
	@Override
	public int getLogLevel() {
		
		return logLevel;
	}
	@Override
	public String getOnCompletion() {
		
		return onCompletion;
	}
	@Override
	public int getNodeCount() {
		
		return nodeCount;
	}
	
	@Override
	public String getFlavor() {
		return flavor;
	}
	@Override
	public void setFlavor(String str) {
		flavor = str;
	}

	
	@Override
	public int getWaitFor() {
		
		return waitFor;
	}

	@Override
	public int getInPort() {
		return inPort;
	}

	@Override
	public int getOutPort() {
		return outPort;
	}

	@Override
	public int getStartSomClients() {
		return startSomClients;
	}

	@Override
	public Object getArgumentsObj() {
		return argumentsObj;
	}

	@Override
	public String getSourceFileByListingFile() {
		return sourceFileByListingFile;
	}

	@Override
	public String getSourceFileDedicated() {
		return sourceFileDedicated;
	}

	@Override
	public String getSourceFilesByFolder() {
		return sourceFilesByFolder;
	}

}
